/**
 * @file wa_menu.c
 *
 */

 /*********************
 *      INCLUDES
 *********************/
#include "wa_conf.h"
#if USE_WA_MENU != 0
#include "wa_menu.h"
#include "wa_pid.h"
#include "wa_input.h"

 /*********************
 *      DEFINES
 *********************/

 /**********************
 *      TYPEDEFS
 **********************/

 /**********************
 *  STATIC PROTOTYPES
 **********************/
static wa_obj_callback ancestor_callback;
static void wa_menu_callback(wa_sign* sign);

static void wa_menu_invalidate_scroll_bar(wa_obj* menu);
static void wa_menu_invalidate_cursor(wa_obj* menu);
static void wa_menu_invalidate_directory(wa_obj* menu);
static void wa_menu_select_to_target_param(wa_obj* menu, uint32_t select, int32_t* target_pos, int32_t* target_cursor_pos_x, int32_t* target_cursor_pos_y, uint32_t* target_cursor_width, uint32_t* target_scroll_bar_block_pos);
#if USE_WA_ANIM != 0
static void wa_menu_anim_exec_cb(void* obj, int32_t value);
static void wa_menu_anim_finish_cb(wa_anim* anim);
static void wa_menu_anim_start_cb(wa_anim* anim);
#endif
/**********************
 *  STATIC VARIABLES
 **********************/

 /**********************
 *      MACROS
 **********************/

 /**********************
 *   GLOBAL FUNCTIONS
 **********************/
 /**
 * menu module init
 */
void wa_menu_module_init(void)
{
}

/**
* create a menu
* @param par
* @return menu obj
*/
wa_obj* wa_menu_create(wa_obj* par)
{
	wa_obj* new_menu;
	wa_menu_ext* menu_ext;

	new_menu = wa_obj_create(par, NULL);
	if (new_menu == NULL)
	{
		return new_menu;
	}

	if (ancestor_callback == NULL)
	{
		ancestor_callback = wa_obj_get_callback(new_menu);
	}
	menu_ext = wa_obj_malloc_ext_attr(new_menu, sizeof(wa_menu_ext));
	if (menu_ext == NULL)
	{
		return NULL;
	}
#if USE_WA_ANIM != 0
	menu_ext->anim = wa_anim_create();
#endif
	menu_ext->anim_enable = wa_nok;
	menu_ext->switch_time_length = 500;

	menu_ext->start_pos = 0;
	menu_ext->end_pos = 0;
	menu_ext->start_cursor_pos_x = 0;
	menu_ext->end_cursor_pos_x = 0;
	menu_ext->start_cursor_pos_y = 0;
	menu_ext->end_cursor_pos_y = 0;
	menu_ext->start_cursor_width = 0;
	menu_ext->end_cursor_width = 0;
	menu_ext->start_scroll_bar_block_pos = 0;
	menu_ext->end_scroll_bar_block_pos = 0;

	menu_ext->font = NULL;
	menu_ext->option_text = NULL;
	menu_ext->option_front_text = NULL;
	menu_ext->option_cnt = 0;

	menu_ext->select = 0;
	menu_ext->space = 0;
	menu_ext->pos = 0;
	menu_ext->dire_type = WA_MENU_DIRE_TYPE_NORMAL;
	menu_ext->dire_offset = 0;

	menu_ext->cursor_width = 0;
	menu_ext->cursor_hegiht = 0;
	menu_ext->cursor_pos_x = 0;
	menu_ext->cursor_pos_y = 0;
	menu_ext->cursor_r = 2;

	menu_ext->cursor_limit_min = 0;
	menu_ext->cursor_limit_max = 0;

	menu_ext->scroll_bar_enable = wa_nok;
	menu_ext->scroll_bar_line_width = 1;
	menu_ext->scroll_bar_block_width = 3;
	menu_ext->scroll_bar_block_height = 0;
	menu_ext->scroll_bar_block_pos = 0;

	menu_ext->fore_color = WA_COLOR_FOREGROUND;
	menu_ext->back_color = WA_COLOR_BACKGROUND;

	wa_obj_set_callback(new_menu, wa_menu_callback);

	return new_menu;
}

/**
* menu add option
* @param menu
* @param text
* @param front_text
*/
void wa_menu_add_option(wa_obj* menu, char* text, char* front_text)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	if ((text == NULL) || (front_text == NULL))
	{
		return;
	}
	if (ext->option_text == NULL)
	{
		ext->option_text = wa_malloc(sizeof(char*));
		ext->option_text[0] = wa_malloc(strlen((const char*)text) + 1);
		strcpy((char*)(ext->option_text[0]), (char*)text);
		ext->option_front_text = wa_malloc(sizeof(char*));
		ext->option_front_text[0] = wa_malloc(strlen((const char*)front_text) + 1);
		strcpy((char*)(ext->option_front_text[0]), (char*)front_text);
		ext->option_cnt = 1;
	}
	else
	{
		char** p;
		ext->option_cnt++;
		p = wa_malloc(sizeof(char*) * ext->option_cnt);
		memcpy(p, ext->option_text, sizeof(char*) * (ext->option_cnt - 1));
		wa_free(ext->option_text);
		ext->option_text = p;
		ext->option_text[ext->option_cnt - 1] = wa_malloc(strlen((const char*)text) + 1);
		strcpy((char*)(ext->option_text[ext->option_cnt - 1]), (char*)text);

		p = wa_malloc(sizeof(char*) * ext->option_cnt);
		memcpy(p, ext->option_front_text, sizeof(char*) * (ext->option_cnt - 1));
		wa_free(ext->option_front_text);
		ext->option_front_text = p;
		ext->option_front_text[ext->option_cnt - 1] = wa_malloc(strlen((const char*)front_text) + 1);
		strcpy((char*)(ext->option_front_text[ext->option_cnt - 1]), (char*)front_text);
	}
	ext->scroll_bar_block_height = wa_obj_get_height(menu) / ext->option_cnt;
	wa_obj_invalidate(menu);
}

/**
* menu clear option
* @param menu
*/
void wa_menu_clear_option(wa_obj* menu)
{
	uint32_t i;
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	if ((ext->option_text == NULL) || (ext->option_front_text == NULL))
	{
		return;
	}
	for (i = 0; i < ext->option_cnt; i++)
	{
		wa_free(ext->option_text[i]);
		wa_free(ext->option_front_text[i]);
	}
	wa_free(ext->option_text);
	wa_free(ext->option_front_text);
	ext->scroll_bar_block_height = wa_obj_get_height(menu);
	wa_obj_invalidate(menu);
}

/**
* menu set select
* @param menu
* @param select
*/
void wa_menu_set_select(wa_obj* menu, uint32_t select)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	if (select == ext->select)
	{
		return;
	}
	if (select <= 0)
	{
		select = 0;
	}
	if (select > (ext->option_cnt - 1))
	{
		select = (ext->option_cnt - 1);
	}
#if USE_WA_ANIM != 0
	if (ext->anim_enable == wa_ok)
	{
		ext->start_pos = ext->pos;
		ext->start_cursor_pos_x = ext->cursor_pos_x;
		ext->start_cursor_pos_y = ext->cursor_pos_y;
		ext->start_cursor_width = ext->cursor_width;
		ext->start_scroll_bar_block_pos = ext->end_scroll_bar_block_pos;
		wa_menu_select_to_target_param(menu, select, &(ext->end_pos), &(ext->end_cursor_pos_x), &(ext->end_cursor_pos_y), (uint32_t*)&(ext->end_cursor_width), (uint32_t*)&(ext->end_scroll_bar_block_pos));
		wa_menu_invalidate_cursor(menu);
		if (ext->start_pos != ext->end_pos)
		{
			wa_menu_invalidate_directory(menu);
		}
		if (ext->scroll_bar_enable == wa_ok)
		{
			wa_menu_invalidate_scroll_bar(menu);
		}
		ext->select = select;
		wa_wm_notify_parent(menu, WA_SIGNAL_NOTIFY_SEL_CHANGED);

		wa_anim_set_var(ext->anim, menu);
		wa_anim_set_exec_cb(ext->anim, wa_menu_anim_exec_cb);
		wa_anim_set_start_cb(ext->anim, wa_menu_anim_start_cb);
		wa_anim_set_finish_cb(ext->anim, wa_menu_anim_finish_cb);
		wa_anim_set_path_cb(ext->anim, wa_anim_path_ease_in_out);
		wa_anim_set_time_long(ext->anim, ext->switch_time_length);
		wa_anim_set_early_apply(ext->anim, wa_nok);
		wa_anim_set_values(ext->anim, 0, 1000);
		wa_anim_start(ext->anim);
	}
	else
#endif
	{
		ext->start_pos = ext->pos;
		wa_menu_select_to_target_param(menu, select, &(ext->end_pos), &(ext->end_cursor_pos_x), &(ext->end_cursor_pos_y), (uint32_t*)&(ext->end_cursor_width), (uint32_t*)&(ext->end_scroll_bar_block_pos));
		wa_menu_invalidate_cursor(menu);
		if (ext->start_pos != ext->end_pos)
		{
			wa_menu_invalidate_directory(menu);
		}
		if (ext->scroll_bar_enable == wa_ok)
		{
			wa_menu_invalidate_scroll_bar(menu);
		}
		ext->pos = ext->end_pos;
		ext->cursor_pos_x = ext->end_cursor_pos_x;
		ext->cursor_pos_y = ext->end_cursor_pos_y;
		ext->cursor_width = ext->end_cursor_width;
		ext->scroll_bar_block_pos = ext->end_scroll_bar_block_pos;

		ext->select = select;
		wa_wm_notify_parent(menu, WA_SIGNAL_NOTIFY_SEL_CHANGED);
		wa_menu_invalidate_cursor(menu);
		if (ext->start_pos != ext->end_pos)
		{
			wa_menu_invalidate_directory(menu);
		}
		if (ext->scroll_bar_enable == wa_ok)
		{
			wa_menu_invalidate_scroll_bar(menu);
		}
	}
}

/**
* menu get select
* @param menu
* @return select
*/
uint32_t wa_menu_get_select(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->select;
}

/**
* menu set font
* @param menu
* @param font
*/
void wa_menu_set_font(wa_obj* menu, wa_font* font)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->font = font;
	ext->cursor_hegiht = wa_font_get_height(font);
	wa_obj_invalidate(menu);
}

/**
* menu get font
* @param menu
* @return font
*/
wa_font* wa_menu_get_font(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->font;
}

/**
* menu set space
* @param menu
* @param space
*/
void wa_menu_set_space(wa_obj* menu, uint32_t space)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->space = space;
	wa_obj_invalidate(menu);
}

/**
* menu get space
* @param menu
* @return space
*/
uint32_t wa_menu_get_space(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->space;
}

/**
* menu set dire_type
* @param menu
* @param dire_type
*/
void wa_menu_set_dire_type(wa_obj* menu, wa_menu_directory_type dire_type)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->dire_type = dire_type;
	wa_obj_invalidate(menu);
}

/**
* menu get dire_type
* @param menu
* @return dire_type
*/
wa_menu_directory_type wa_menu_get_dire_type(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->dire_type;
}

/**
* menu set dire_offset
* @param menu
* @param dire_offset
*/
void wa_menu_set_dire_offset(wa_obj* menu, uint8_t dire_offset)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->dire_offset = dire_offset;
	wa_obj_invalidate(menu);
}

/**
* menu get dire_offset
* @param menu
* @return dire_offset
*/
uint8_t wa_menu_get_dire_offset(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->dire_offset;
}

/**
* menu set cursor_r
* @param menu
* @param cursor_r
*/
void wa_menu_set_cursor_r(wa_obj* menu, uint8_t cursor_r)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->cursor_r = cursor_r;
	wa_obj_invalidate(menu);
}

/**
* menu get cursor_r
* @param menu
* @return cursor_r
*/
uint8_t wa_menu_get_cursor_r(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->cursor_r;
}

/**
* menu set cursor_limit_min
* @param menu
* @param cursor_limit_min
*/
void wa_menu_set_cursor_limit_min(wa_obj* menu, uint32_t cursor_limit_min)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->cursor_limit_min = cursor_limit_min;
	wa_obj_invalidate(menu);
}

/**
* menu get cursor_limit_min
* @param menu
* @return cursor_limit_min
*/
uint32_t wa_menu_get_cursor_limit_min(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->cursor_limit_min;
}

/**
* menu set cursor_limit_max
* @param menu
* @param cursor_limit_max
*/
void wa_menu_set_cursor_limit_max(wa_obj* menu, uint32_t cursor_limit_max)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->cursor_limit_max = cursor_limit_max;
	wa_obj_invalidate(menu);
}

/**
* menu get cursor_limit_max
* @param menu
* @return cursor_limit_max
*/
uint32_t wa_menu_get_cursor_limit_max(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->cursor_limit_max;
}

/**
* menu set scroll_bar_enable
* @param menu
* @param scroll_bar_enable
*/
void wa_menu_set_scroll_bar_enable(wa_obj* menu, wa_result scroll_bar_enable)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->scroll_bar_enable = scroll_bar_enable;
	wa_obj_invalidate(menu);
}

/**
* menu get scroll_bar_enable
* @param menu
* @return scroll_bar_enable
*/
wa_result wa_menu_get_scroll_bar_enable(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->scroll_bar_enable;
}

/**
* menu set scroll_bar_line_width
* @param menu
* @param scroll_bar_line_width
*/
void wa_menu_set_scroll_bar_line_width(wa_obj* menu, uint32_t scroll_bar_line_width)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->scroll_bar_line_width = scroll_bar_line_width;
	wa_obj_invalidate(menu);
}

/**
* menu get scroll_bar_line_width
* @param menu
* @return scroll_bar_line_width
*/
uint32_t wa_menu_get_scroll_bar_line_width(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->scroll_bar_line_width;
}

/**
* menu set scroll_bar_block_width
* @param menu
* @param scroll_bar_block_width
*/
void wa_menu_set_scroll_bar_block_width(wa_obj* menu, uint32_t scroll_bar_block_width)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->scroll_bar_block_width = scroll_bar_block_width;
	wa_obj_invalidate(menu);
}

/**
* menu get scroll_bar_block_width
* @param menu
* @return scroll_bar_block_width
*/
uint32_t wa_menu_get_scroll_bar_block_width(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->scroll_bar_block_width;
}

/**
* menu set fore_color
* @param menu
* @param fore_color
*/
void wa_menu_set_fore_color(wa_obj* menu, wa_gui_color fore_color)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->fore_color = fore_color;
	wa_obj_invalidate(menu);
}

/**
* menu get fore_color
* @param menu
* @return fore_color
*/
wa_gui_color wa_menu_get_fore_color(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->fore_color;
}

/**
* menu set back_color
* @param menu
* @param back_color
*/
void wa_menu_set_back_color(wa_obj* menu, wa_gui_color back_color)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->back_color = back_color;
	wa_obj_invalidate(menu);
}

/**
* menu get back_color
* @param menu
* @return back_color
*/
wa_gui_color wa_menu_get_back_color(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	return ext->back_color;
}

/**
*menu set anim enable
* @param menu
* @param anim enable
*/
void wa_menu_set_anim_enable(wa_obj * menu, wa_result anim_enable)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->anim_enable = anim_enable;
}

/**
* menu set swtich time length
* @param menu
* @param time length
*/
void wa_menu_set_switch_time_length(wa_obj* menu, uint32_t length)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	ext->switch_time_length = length;
}

/**
* menu init select pos
* @param menu
*/
void wa_menu_init_select_pos(wa_obj* menu)
{
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	wa_menu_select_to_target_param(menu, ext->select, &(ext->pos), &(ext->cursor_pos_x), &(ext->cursor_pos_y), &(ext->cursor_width), &(ext->scroll_bar_block_pos));
	wa_obj_invalidate(menu);
}

/**********************
*   STATIC FUNCTIONS
**********************/
#if USE_WA_ANIM != 0
/**
* menu anim exec cb
* @param obj
* @param value
*/
static void wa_menu_anim_exec_cb(void* obj, int32_t value)
{
	wa_obj* menu = (wa_obj*)obj;
	wa_menu_ext* ext = wa_obj_get_ext_attr(menu);
	//directory
	if (ext->start_pos != ext->end_pos)
	{
		wa_menu_invalidate_directory(menu);
		ext->pos = ext->start_pos + (ext->end_pos - ext->start_pos) * (value - ext->anim->start_value) / (ext->anim->end_value - ext->anim->start_value);
		wa_menu_invalidate_directory(menu);
	}
	//scroll bar
	if (ext->scroll_bar_enable == wa_ok)
	{
		ext->scroll_bar_block_pos = ext->start_scroll_bar_block_pos + (ext->end_scroll_bar_block_pos - ext->start_scroll_bar_block_pos) 
									* (value - ext->anim->start_value) / (ext->anim->end_value - ext->anim->start_value);
		wa_menu_invalidate_scroll_bar(menu);
	}
	//cursor
	wa_menu_invalidate_cursor(menu);
	ext->cursor_pos_x = ext->start_cursor_pos_x + (ext->end_cursor_pos_x - ext->start_cursor_pos_x)
				* (value - ext->anim->start_value) / (ext->anim->end_value - ext->anim->start_value);
	ext->cursor_pos_y = ext->start_cursor_pos_y + (ext->end_cursor_pos_y - ext->start_cursor_pos_y)
				* (value - ext->anim->start_value) / (ext->anim->end_value - ext->anim->start_value);
	ext->cursor_width = ext->start_cursor_width + (ext->end_cursor_width - ext->start_cursor_width)
				* (value - ext->anim->start_value) / (ext->anim->end_value - ext->anim->start_value);
	wa_menu_invalidate_cursor(menu);

}

/**
* menu anim finish cb
* @param anim
*/
static void wa_menu_anim_finish_cb(wa_anim* anim)
{
	wa_obj* menu = (wa_obj*)(anim->var);
	wa_menu_ext* ext = wa_obj_get_ext_attr(menu);
	if (ext->pid == wa_ok)
	{
		wa_pid_enable(wa_ok);
	}
	if (ext->input == wa_ok)
	{
		wa_input_enable(wa_ok);
	}
}

/**
* menu anim start cb
* @param anim
*/
static void wa_menu_anim_start_cb(wa_anim* anim)
{
	wa_obj* menu = (wa_obj*)(anim->var);
	wa_menu_ext* ext = wa_obj_get_ext_attr(menu);
	if (wa_pid_is_enable() == wa_ok)
	{
		ext->pid = wa_ok;
		wa_pid_enable(wa_nok);
	}
	else
	{
		ext->pid = wa_nok;
	}

	if (wa_input_is_enable() == wa_ok)
	{
		ext->input = wa_ok;
		wa_input_enable(wa_nok);
	}
	else
	{
		ext->input = wa_nok;
	}
}

#endif

/**
* menu find min voption
* @param menu
* @return min voption
*/
static int32_t wa_menu_find_min_voption(wa_obj* menu)
{
	int h = 0;
	int fh;
	uint32_t i;
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	fh = wa_font_get_height(ext->font);
	for (i = 0; i < ext->option_cnt; i++)
	{
		h += fh;
		if ((-ext->pos) < h)
		{
			return i;
		}
		h += ext->space;
	}
	return -1;
}

/**
* menu find max voption
* @param menu
* @return max voption
*/
static int32_t wa_menu_find_max_voption(wa_obj* menu)
{
	int h = 0;
	int fh;
	int height;
	uint32_t i;
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	fh = wa_font_get_height(ext->font);
	height = wa_obj_get_height(menu);
	for (i = 0; i < ext->option_cnt; i++)
	{
		h += fh;
		if ((-ext->pos + height) <= h)
		{
			return i;
		}
		h += ext->space;
	}
	return (ext->option_cnt - 1);
}

/**
* menu invalidate scroll bar
* @param menu
*/
static void wa_menu_invalidate_scroll_bar(wa_obj* menu)
{
	wa_area area;
	wa_menu_ext* ext;
	uint32_t pw;
	ext = wa_obj_get_ext_attr(menu);
	if (ext->scroll_bar_enable == wa_ok)
	{
		pw = wa_obj_get_width(menu);
		area.x1 = menu->rect.x1 + pw - ext->scroll_bar_block_width;
		area.y1 = menu->rect.y1;
		area.x2 = menu->rect.x2;
		area.y2 = menu->rect.y2;
		wa_obj_part_invalidate(menu, &area);
	}
}

/**
* menu invalidate cursor
* @param menu
*/
static void wa_menu_invalidate_cursor(wa_obj* menu)
{
	wa_area area;
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(menu);
	area.x1 = menu->rect.x1 + ext->cursor_pos_x;
	area.y1 = menu->rect.y1 + ext->cursor_pos_y;
	area.x2 = area.x1 + ext->cursor_width - 1;
	area.y2 = area.y1 + ext->cursor_hegiht - 1;
	wa_obj_part_invalidate(menu, &area);
}

/**
* menu invalidate directory
* @param menu
*/
static void wa_menu_invalidate_directory(wa_obj* menu)
{
	wa_area area;
	wa_menu_ext* ext;
	int32_t min, max;
	uint8_t i;
	uint32_t fw, fh;
	uint32_t tw, th;
	uint32_t str_length_max = 0;
	ext = wa_obj_get_ext_attr(menu);
	min = wa_menu_find_min_voption(menu);
	max = wa_menu_find_max_voption(menu);
	for (i = min; i <= max; i++)
	{
		uint32_t len;
		char* p = ext->option_front_text[i];
		wa_font_get_str_size((int*)&fw, (int*)&fh, (uint8_t*)p, ext->font, 0, 0);
		p = ext->option_text[i];
		wa_font_get_str_size((int*)&tw, (int*)&th, (uint8_t*)p, ext->font, 0, 0);
		len = fw + tw + 2 * ext->cursor_r;
		if (str_length_max < len)
		{
			str_length_max = len;
		}
	}
	if (ext->dire_type == WA_MENU_DIRE_TYPE_NORMAL)
	{
		area.x1 = menu->rect.x1 + ext->dire_offset;
		area.y1 = menu->rect.y1;
		area.x2 = area.x1 + str_length_max - 1;
		area.y2 = menu->rect.y2;
		wa_obj_part_invalidate(menu, &area);
	}
	else if (ext->dire_type == WA_MENU_DIRE_TYPE_CENTER)
	{
		if (ext->scroll_bar_enable == wa_nok)
		{
			area.x1 = menu->rect.x1 + (wa_obj_get_width(menu) - str_length_max) / 2 + ext->dire_offset;
			area.y1 = menu->rect.y1;
			area.x2 = area.x1 + str_length_max - 1;
			area.y2 = menu->rect.y2;
		}
		else
		{
			area.x1 = menu->rect.x1 + (wa_obj_get_width(menu) - ext->scroll_bar_block_width - str_length_max) / 2 + ext->dire_offset;
			area.y1 = menu->rect.y1;
			area.x2 = area.x1 + str_length_max - 1;
			area.y2 = menu->rect.y2;
		}
		wa_obj_part_invalidate(menu, &area);
	}
}

/**
* menu select to pos
* @param menu
* @return pos
*/
static void wa_menu_select_to_target_param(wa_obj* menu, uint32_t select, int32_t* target_pos, int32_t* target_cursor_pos_x, int32_t* target_cursor_pos_y, uint32_t* target_cursor_width, uint32_t* target_scroll_bar_block_pos)
{
	wa_menu_ext* ext;
	uint32_t obj_h;
	uint32_t font_height;
	int32_t cursor_move_distance;
	uint8_t move_dir;
	int32_t cursor_can_move_distance;
	int32_t dire_can_move_distance;
	uint32_t dire_height;
	uint32_t diff;
	char* fp;
	char* tp;
	uint32_t fw, fh;
	uint32_t tw, th;
	ext = wa_obj_get_ext_attr(menu);

	if (select == ext->select)
	{
		//return current value
		*target_pos = ext->pos;;
		*target_cursor_pos_y = ext->cursor_pos_y;
		goto GET_OTHER;
	}

	obj_h = wa_obj_get_height(menu);
	font_height = wa_font_get_height(ext->font);
	dire_height = ext->option_cnt * font_height + (ext->option_cnt - 1) * ext->space;
	cursor_move_distance = ((int32_t)select - (int32_t)ext->select) * (font_height + ext->space);
	if (cursor_move_distance > 0)
	{
		move_dir = 1;
		cursor_can_move_distance = (int32_t)ext->cursor_limit_max - (int32_t)ext->cursor_pos_y - (int32_t)font_height;
		if (cursor_can_move_distance < 0)
		{
			cursor_can_move_distance = 0;
		}
		dire_can_move_distance = (int32_t)dire_height - (int32_t)obj_h + ext->pos;
		if (dire_can_move_distance < 0)
		{
			dire_can_move_distance = 0;
		}
	}
	else
	{
		cursor_move_distance = -cursor_move_distance;
		move_dir = 0;
		cursor_can_move_distance = (int32_t)ext->cursor_pos_y - (int32_t)ext->cursor_limit_min;
		if (cursor_can_move_distance < 0)
		{
			cursor_can_move_distance = 0;
		}
		dire_can_move_distance =  - ext->pos;
		if (dire_can_move_distance < 0)
		{
			dire_can_move_distance = 0;
		}
	}
	if (cursor_move_distance <= cursor_can_move_distance)
	{
		if (move_dir == 1)
		{
			*target_cursor_pos_y = ext->cursor_pos_y + cursor_move_distance;
		}
		else
		{
			*target_cursor_pos_y = ext->cursor_pos_y - cursor_move_distance;
		}
		*target_pos = ext->pos;
	}
	else if ((cursor_move_distance > cursor_can_move_distance) && (cursor_move_distance <= (cursor_can_move_distance + dire_can_move_distance)))
	{
		diff = cursor_move_distance - cursor_can_move_distance;
		if (move_dir == 1)
		{
			*target_cursor_pos_y = ext->cursor_pos_y + cursor_can_move_distance;
			*target_pos = ext->pos - diff;
		}
		else
		{
			*target_cursor_pos_y = ext->cursor_pos_y - cursor_can_move_distance;
			*target_pos = ext->pos + diff;
		}
	}
	else if (cursor_move_distance > (cursor_can_move_distance + dire_can_move_distance))
	{
		diff = cursor_move_distance - dire_can_move_distance;
		if (move_dir == 1)
		{
			*target_pos = ext->pos - dire_can_move_distance;
			*target_cursor_pos_y = ext->cursor_pos_y + diff;
		}
		else
		{
			*target_pos = ext->pos + dire_can_move_distance;
			*target_cursor_pos_y = ext->cursor_pos_y - diff;
		}
	}

GET_OTHER:
	*target_scroll_bar_block_pos = select * ext->scroll_bar_block_height;

	fp = ext->option_front_text[select];
	tp = ext->option_text[select];
	wa_font_get_str_size((int*)&fw, (int*)&fh, (uint8_t*)fp, ext->font, 0, 0);
	wa_font_get_str_size((int*)&tw, (int*)&th, (uint8_t*)tp, ext->font, 0, 0);

	*target_cursor_width = tw + 2 * ext->cursor_r;
	if (ext->dire_type == WA_MENU_DIRE_TYPE_NORMAL)
	{
		*target_cursor_pos_x = ext->dire_offset + fw;
	}
	else if (ext->dire_type == WA_MENU_DIRE_TYPE_CENTER)
	{
		int x, opw;
		int w = wa_obj_get_width(menu);
		opw = fw + tw + 2 * ext->cursor_r;
		if (ext->scroll_bar_enable == wa_ok)
		{
			x = (w - ext->scroll_bar_block_width - opw) / 2 + ext->dire_offset;
		}
		else
		{
			x = (w - opw) / 2 + ext->dire_offset;
		}
		*target_cursor_pos_x = x + fw;
	}
}

/**
* menu redraw functions
* @param obj
*/
static void _Paint(wa_obj* obj)
{
	int32_t pw, ph;
	wa_menu_ext* ext;
	uint32_t min, max;
	uint32_t next_len;
	uint32_t i;
	int font_h;
	int32_t start_pos;
	wa_area str_area;
	int32_t x = 0, y = 0;
	int32_t srx;

	ext = wa_obj_get_ext_attr(obj);
	pw = wa_obj_get_width(obj);
	ph = wa_obj_get_height(obj);
	wa_mask_fillrect(obj->rect.x1, obj->rect.y1,
		pw,
		ph,
		&(obj->redraw_rect),
		ext->back_color);
	if (ext->option_cnt == 0)
	{
		return;
	}
	if (ext->option_text == NULL)
	{
		return;
	}
	if (ext->option_front_text == NULL)
	{
		return;
	}
	if (ext->font == NULL)
	{
		return;
	}

	//directory
	min = wa_menu_find_min_voption(obj);
	max = wa_menu_find_max_voption(obj);

	next_len = 0;
	font_h = wa_font_get_height(ext->font);
	for (i = 0; i < min; i++)
	{
		next_len += font_h;
		next_len += ext->space;
	}
	start_pos = next_len + ext->pos;
	for (i = min; i <= max; i++)
	{
		int fw, fh, tw, th;
		int opw = 0;
		char* fp = ext->option_front_text[i];
		char* tp = ext->option_text[i];
		wa_font_get_str_size(&fw, &fh, (uint8_t*)fp, ext->font, 0, 0);
		wa_font_get_str_size(&tw, &th, (uint8_t*)tp, ext->font, 0, 0);
		opw = fw + tw + 2 * ext->cursor_r;
		if (ext->dire_type == WA_MENU_DIRE_TYPE_NORMAL)
		{
			x = ext->dire_offset;
			y = start_pos;
		}
		else if (ext->dire_type == WA_MENU_DIRE_TYPE_CENTER)
		{
			if (ext->scroll_bar_enable == wa_ok)
			{
				x = (pw - ext->scroll_bar_block_width - opw) / 2 + ext->dire_offset;
			}
			else
			{
				x = (pw - opw) / 2 + ext->dire_offset;
			}
			y = start_pos;
		}

		str_area.x1 = obj->rect.x1 + x;
		str_area.y1 = obj->rect.y1 + y;
		str_area.x2 = str_area.x1 + fw - 1;
		str_area.y2 = str_area.y1 + fh - 1;
		wa_mask_dispstr_1(&str_area, (uint8_t*)fp, ext->font, 0, 0, &(obj->redraw_rect), ext->fore_color);

		str_area.x1 = obj->rect.x1 + x + fw + ext->cursor_r;
		str_area.y1 = obj->rect.y1 + y;
		str_area.x2 = str_area.x1 + tw - 1;
		str_area.y2 = str_area.y1 + th - 1;
		wa_mask_dispstr_1(&str_area, (uint8_t*)tp, ext->font, 0, 0, &(obj->redraw_rect), ext->fore_color);

		start_pos += th;
		start_pos += ext->space;
	}

	//scroll_bar
	if (ext->scroll_bar_enable == wa_ok)
	{
		srx = pw - ext->scroll_bar_block_width + (ext->scroll_bar_block_width - ext->scroll_bar_line_width) / 2;
		wa_mask_fillrect(obj->rect.x1 + srx, obj->rect.y1, ext->scroll_bar_line_width, ph, &(obj->redraw_rect), ext->fore_color);
		wa_mask_fillrect(obj->rect.x1 + pw - ext->scroll_bar_block_width,
			obj->rect.y1 + ext->scroll_bar_block_pos,
			ext->scroll_bar_block_width,
			ext->scroll_bar_block_height,
			&(obj->redraw_rect), ext->fore_color);
	}

	//cursor
	wa_mask_inverse_roundrect(obj->rect.x1 + ext->cursor_pos_x,
		obj->rect.y1 + ext->cursor_pos_y,
		ext->cursor_width, ext->cursor_hegiht, ext->cursor_r, &(obj->redraw_rect));
}

/**
 * menu obj delete function
 * @param obj
 */
static void _Delete(wa_obj* obj)
{
#if USE_WA_ANIM != 0
	wa_menu_ext* ext;
	ext = wa_obj_get_ext_attr(obj);
	if (ext->anim != NULL)
	{
		wa_free(ext->anim);
	}
#endif
	wa_menu_clear_option(obj);
}

/**
 * menu obj callback function
 * @param sign
 */
static void wa_menu_callback(wa_sign* sign)
{
	int sign_id;
	wa_obj* obj;

	ancestor_callback(sign);
	sign_id = sign->sign_id;
	obj = sign->obj_dst;

	switch (sign_id)
	{
		case WA_SIGNAL_PAINT:
		{
			_Paint(obj);
			break;
		}
		case WA_SIGNAL_DELETE:
		{
			_Delete(obj);
			break;
		}
		default:
		{
			break;
		}
	}
}

#endif
